#include "stdafx.h"
#include <MyControls/TransformManip.h>
#include <MyControls/TransformEditor.h>
#include <MyControls/Action.h>

namespace My {

void SnapToLine(const point<float> & start, point<float> & current,
	const point<double> & target, const point<double> & p, const point<double> & n, double h)
{
	point<float> move = current - start;
	// (p, v) define the target line
	point<double> newTarget = target + point<double>(move);
	point<double> projection = newTarget - n * scalar_product(newTarget - p, n);
	if (abs(projection - newTarget) < h)
		current += point<float>(projection - newTarget);
}

// TransformMove

TransformMove::TransformMove(TransformEditor & editor, CPoint p) :
	m_Editor(editor)
{
	assert(m_Editor.IsOpen());
	m_Old = m_Editor.GetTransform();
	rect<double> rBounds = m_Editor.GetTransformOwner()->GetBounds();
	point<double> pp[4];
	pp[0] = m_Old(point<double>(rBounds.x1, rBounds.y2));
	pp[1] = m_Old(point<double>(rBounds.x1, rBounds.y1));
	pp[2] = m_Old(point<double>(rBounds.x2, rBounds.y1));
	pp[3] = m_Old(point<double>(rBounds.x2, rBounds.y2));
	m_Bounds.x1 = m_Bounds.x2 = pp[0].x;
	m_Bounds.y1 = m_Bounds.y2 = pp[0].y;
	for (int i = 1; i < 4; ++i) {
		if (pp[i].x < m_Bounds.x1)
			m_Bounds.x1 = pp[i].x;
		if (pp[i].x > m_Bounds.x2)
			m_Bounds.x2 = pp[i].x;
		if (pp[i].y < m_Bounds.y1)
			m_Bounds.y1 = pp[i].y;
		if (pp[i].y > m_Bounds.y2)
			m_Bounds.y2 = pp[i].y;
	}
	m_Margins = m_Editor.GetTransformOwner()->GetMargins();
	m_Start = m_Editor.GetBench().ClientToBench(p);
	m_Editor.GetBench().SetCapture();
}

void TransformMove::Manipulate(UINT nFlags, CPoint p)
{
	point<float> fp = m_Editor.GetBench().ClientToBench(p);

	if (!(nFlags & MK_CONTROL)) {
		SnapToLine(m_Start, fp, (m_Bounds.p1() + m_Bounds.p2()) / 2, 
			(m_Margins.p1() + m_Margins.p2()) / 2, point<double>(1, 0), 4 / m_Editor.GetBench().GetScale()); 
		SnapToLine(m_Start, fp, (m_Bounds.p1() + m_Bounds.p2()) / 2, 
			(m_Margins.p1() + m_Margins.p2()) / 2, point<double>(0, 1), 4 / m_Editor.GetBench().GetScale());
		SnapToLine(m_Start, fp, m_Bounds.p1(), 
			m_Margins.p1(), point<double>(1, 0), 4 / m_Editor.GetBench().GetScale()); 
		SnapToLine(m_Start, fp, m_Bounds.p1(), 
			m_Margins.p1(), point<double>(0, 1), 4 / m_Editor.GetBench().GetScale()); 
		SnapToLine(m_Start, fp, m_Bounds.p2(), 
			m_Margins.p2(), point<double>(1, 0), 4 / m_Editor.GetBench().GetScale()); 
		SnapToLine(m_Start, fp, m_Bounds.p2(), 
			m_Margins.p2(), point<double>(0, 1), 4 / m_Editor.GetBench().GetScale());
	}

	point<float> move = fp - m_Start;

	Affine t = m_Old;
	t.x += move.x;
	t.y += move.y;
	m_Editor.SetTransform(t);
	m_Editor.GetBench().Invalidate();
}

void TransformMove::Apply(UINT nFlags, CPoint p)
{
	Manipulate(nFlags, p);
	m_Editor.Synchronize();
	ReleaseCapture();
	m_Editor.GetBench().Invalidate();
}

void TransformMove::PaintRect(CDC *, const CRect &)
{
}

// TransformRotate

TransformRotate::TransformRotate(TransformEditor & editor, CPoint p) :
	m_Editor(editor)
{
	assert(m_Editor.IsOpen());
	m_Old = m_Editor.GetTransform();
	rect<double> rBounds = m_Editor.GetTransformOwner()->GetBounds();
	m_Anchor = point<double>((rBounds.x1 + rBounds.x2) / 2, (rBounds.y1 + rBounds.y2) / 2);
	m_Start = m_Editor.GetBench().ClientToBench(p);
	m_Editor.GetBench().SetCapture();
}

void TransformRotate::Manipulate(UINT nFlags, CPoint p)
{
	point<double> oldAnchor = m_Old(m_Anchor);
	point<float> v0 = m_Start - point<float>(oldAnchor);
	point<float> v = m_Editor.GetBench().ClientToBench(p) - point<float>(oldAnchor);
	double alpha = angle(v0, v);

	if (!(nFlags & MK_CONTROL) && fabs(alpha) < 0.01)
		alpha = 0;

	Affine rotate;
	rotate.xx = rotate.yy = cos(alpha);
	rotate.xy = - sin(alpha);
	rotate.yx = - rotate.xy;
	rotate.x = 0;
	rotate.y = 0;

	Affine t = rotate * m_Old;
	point<double> newAnchor = t(m_Anchor);
	t.x -= (newAnchor.x - oldAnchor.x);
	t.y -= (newAnchor.y - oldAnchor.y);

	m_Editor.SetTransform(t);
	m_Editor.GetBench().Invalidate();
}

void TransformRotate::Apply(UINT nFlags, CPoint p)
{
	Manipulate(nFlags, p);
	m_Editor.Synchronize();
	ReleaseCapture();
	m_Editor.GetBench().Invalidate();
}

void TransformRotate::PaintRect(CDC *, const CRect &)
{
}

// CornerPin

CornerPin::CornerPin(TransformEditor & editor, CPoint p, int index) :
	m_Editor(editor), m_Index(index)
{
	assert(m_Editor.IsOpen());
	assert(m_Index >= 0 && m_Index < 3);
	m_Index = index;
	rect<double> rBounds = m_Editor.GetTransformOwner()->GetBounds();
	m_ImageCorners[0] = point<double>(rBounds.x1, rBounds.y2);
	m_ImageCorners[1] = point<double>(rBounds.x1, rBounds.y1);
	m_ImageCorners[2] = point<double>(rBounds.x2, rBounds.y1);
	m_ImageCorners[3] = point<double>(rBounds.x2, rBounds.y2);
	m_Margins = m_Editor.GetTransformOwner()->GetMargins();
	Affine t = m_Editor.GetTransformOwner()->GetTransform();
	for (int i = 0; i < 4; ++i)
		m_BenchCorners[i] = t(m_ImageCorners[i]);
	m_Old = m_BenchCorners[m_Index];
	m_Start = m_Editor.GetBench().ClientToBench(p);
	m_Editor.GetBench().SetCapture();
}

void CornerPin::Manipulate(UINT nFlags, CPoint p)
{
	point<float> fp = m_Editor.GetBench().ClientToBench(p);

	if (!(nFlags & MK_CONTROL)) {
		SnapToLine(m_Start, fp, m_Old, 
			m_Margins.p1(), point<double>(1, 0), 4 / m_Editor.GetBench().GetScale()); 
		SnapToLine(m_Start, fp, m_Old, 
			m_Margins.p1(), point<double>(0, 1), 4 / m_Editor.GetBench().GetScale()); 
		SnapToLine(m_Start, fp, m_Old, 
			m_Margins.p2(), point<double>(1, 0), 4 / m_Editor.GetBench().GetScale()); 
		SnapToLine(m_Start, fp, m_Old, 
			m_Margins.p2(), point<double>(0, 1), 4 / m_Editor.GetBench().GetScale());
	}

	m_BenchCorners[m_Index] = m_Old + point<double>(fp - m_Start);
	m_Editor.CalcTransform(m_ImageCorners, m_BenchCorners);
	m_Editor.GetBench().Invalidate();
}

void CornerPin::Apply(UINT nFlags, CPoint p)
{
	Manipulate(nFlags, p);
	m_Editor.Synchronize();
	ReleaseCapture();
	m_Editor.GetBench().Invalidate();
}

void CornerPin::PaintRect(CDC * pDC, const CRect & rClip)
{
}

} // My